/*
>> ReductionOp.s
>>
>> Pixel reduction operation skeleton, allowing buffer and scalar inputs, and
>> reduced outputs. Adapted from PixelToPixelOp.s.
>>
>> Author: Wouter Caarls
>> Date:   06-04-2005
*/

#include "ilpstrategies.h"

void ReductionOp(argcode_t *argcode, int arguments, code body)
{
int _ii, _jj, _kk;
promote int _fi = -1, _stride;

for (_ii=0; _ii < arguments; _ii++)
{
  argcode[_ii].state.delay = 0;

  if (argcode[_ii].argtype == ARG_STREAM_IN)
  {
    /* Adjust first input */
    if (_fi == -1)
      _fi = _ii;
    
    @declare1d(addpointer(argcode[_ii].datatype), argcode[_ii].id, 1);
    
    body = replace(argcode[_ii].id,
		    replace(`id`, argcode[_ii].id, `&id[0][_ii]`),
		    body);

    argcode[_ii].state.buffer = 1;
    argcode[_ii].state.state = 1024; /* FRAME DELAY */
  }
  else if (argcode[_ii].argtype == ARG_SCALAR_IN || argcode[_ii].argtype == ARG_SCALAR_OUT)
  {
    argcode[_ii].state.buffer = 0;
    argcode[_ii].state.state = 0;

    /* Non-streams must be initialised */
    @declare(argcode[_ii].datatype, argcode[_ii].id);

    body = replacedeep(argcode[_ii].id, `&$@argcode[_ii].id`, body);

    if (@dataclass(argcode[_ii].datatype) == DATACLASS_INTEGER)
      @argcode[_ii].id = (@argcode[_ii].argid).data.d;
    else if (@dataclass(argcode[_ii].datatype) == DATACLASS_FLOAT)
      @argcode[_ii].id = (@argcode[_ii].argid).data.f;
    else
      @argcode[_ii].id = @cast(argcode[_ii].datatype, (@argcode[_ii].argid).data.b);

    if (argcode[_ii].argtype == ARG_SCALAR_OUT)
    {
      int _jj;

      /* Initialise from previous scalar input */
      for (_jj=_ii; _jj >=0; _jj--)
      {
	if (argcode[_jj].argtype == ARG_SCALAR_IN)
	{
	  @argcode[_ii].id = @argcode[_jj].id;
	  break;
	}
      }

      if (_jj < 0)
	error("Uninitialized scalar output", _ii);
    }
  }
  else
    error("Stream outputs not supported in this skeleton");

}
	
_stride = scuBufferGetStride((@argcode[_fi].argid).buffer);

while (scuBufferPeek((@argcode[_fi].argid).buffer, (@argcode[_fi].argid).reader,
		     (void**)&(@argcode[_fi].id)[0], _stride*sizeof(argcode[_fi].datatype),
		     SCU_BUF_BLOCK_ALL) == _stride*sizeof(argcode[_fi].datatype))
{
  /* Peek all inputs */
  for (_ii=0; _ii < arguments; _ii++)
    if (_ii != _fi && argcode[_ii].argtype == ARG_STREAM_IN)
      scuBufferPeek((@argcode[_ii].argid).buffer, (@argcode[_ii].argid).reader, (void**)&(@argcode[_ii].id)[0],
		 _stride*sizeof(argcode[_ii].datatype), SCU_BUF_BLOCK_ALL);
  
  for (_ii=0; _ii < _stride; _ii++)
  {
    @body;
  
    /* Set input to output */
    for (_kk=0; _kk < arguments; _kk++)
    {
      if (argcode[_kk].argtype == ARG_SCALAR_OUT)
      {
	for (_jj=_kk; _jj >=0; _jj--)
	{
	  if (argcode[_jj].argtype == ARG_SCALAR_IN)
	  {
	    @argcode[_jj].id = @argcode[_kk].id;
	    break;
	  }
	}
      }
    }
  }
  
  /* Release stuff */
  for (_ii=0; _ii < arguments; _ii++)
    if (argcode[_ii].argtype == ARG_STREAM_IN)
      scuBufferReleasePeeked((@argcode[_ii].argid).buffer, (@argcode[_ii].argid).reader, _stride*sizeof(argcode[_ii].datatype));
}  

finalizearguments_direct(argcode, arguments);

/* Write result */
for (_ii=0; _ii < arguments; _ii++)
  if (argcode[_ii].argtype == ARG_SCALAR_OUT)
  {
    if (@dataclass(argcode[_ii].datatype) == DATACLASS_INTEGER)
      ilpWriteResult(op, _ii, SC_VAR_INTEGER, &@argcode[_ii].id);
    else if (@dataclass(argcode[_ii].datatype) == DATACLASS_FLOAT)
      ilpWriteResult(op, _ii, SC_VAR_DOUBLE, &@argcode[_ii].id);
    /* ELSE: POINTER! HELP!*/
  }

  /* Set properties */
  propEnvReset();
  propEnv("operation", operation);
 
  for (_ii=0; _ii < arguments; _ii++)
  {
    propEnv("argument", @idtostring(makeid(`id`, _ii)));
      propSeti("id", _ii);
      propEnv("target", target);

      propSeti("buffer", argcode[_ii].state.buffer);
      propSeti("state", argcode[_ii].state.state);
      propSeti("delay", argcode[_ii].state.delay);

      propSeti("elementsize", sizeof(argcode[_ii].datatype));

      if (argcode[_ii].argtype == ARG_STREAM_IN || argcode[_ii].argtype == ARG_STREAM_OUT)
        propSeti("chunksize", 1024);
      else
        propSeti("chunksize", 1);

      propEnvBack();
    propEnvBack();
  }
}
